package text;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import text.traversal.ITextTraversal;

public class Text
{
	protected final String								name;

	public static final String							OCCURENCE_INFO		= "Occurence";

	protected final Map<String, Double>					infoMap				= new Hashtable<String, Double>();

	protected final Map<String, Map<String, Double>>	wordInfoMap			= new Hashtable<String, Map<String, Double>>();
	protected final Map<String, Double>					occurenceWordMap;
	protected int										wordOccurenceSum	= 0;

	protected final TextSet								textSet;

	public Set<String> getWords()
	{
		return occurenceWordMap.keySet();
	}
	
	public String getName()
	{
		return this.name;
	}

	public TextSet getTextSet()
	{
		return this.textSet;
	}
	
	public int getWordCount()
	{
		return this.occurenceWordMap.size();
	}

	public int getWordOccurenceSum()
	{
		return wordOccurenceSum;
	}

	public double getOccurenceCount(String word)
	{
		Double value = this.occurenceWordMap.get(word);
		if (value == null)
		{
			return 0;
		}
		return value;
	}

	public Double getInfo(String infoName)
	{
		return this.infoMap.get(infoName);
	}

	public Double getInfo(String infoName, String word)
	{
		return this.wordInfoMap.get(infoName).get(word);
	}

	public void addInfo(String infoName, Double value)
	{
		this.infoMap.put(infoName, value);
	}
	
	public void clearWordInfo(String infoName)
	{
		Map<String, Double> map = this.wordInfoMap.get(infoName);
		
		if(map != null)
		{
			map.clear();
		}
	}
	
	public void shareInfo(String infoName, Text text)
	{
		text.wordInfoMap.put(infoName, this.wordInfoMap.get(infoName));
	}
	
	public void renameInfo(String currentName, String newName)
	{
		Map<String, Double> map = this.wordInfoMap.get(currentName);
		
		this.wordInfoMap.put(newName, map);
		
		this.wordInfoMap.remove(currentName);
	}

	public void addInfo(String infoName, String word, Double value)
	{
		Map<String, Double> wordMap = this.wordInfoMap.get(infoName);

		if (wordMap == null)
		{
			wordMap = new Hashtable<String, Double>(this.occurenceWordMap.size());
			this.wordInfoMap.put(infoName, wordMap);
		}

		wordMap.put(word, value);
	}
	
	public void traversal(ITextTraversal traversal)
	{
		Object[] wordTab = occurenceWordMap.keySet().toArray();
		for (int i = 0; i < wordTab.length; i++)
		{
			traversal.traversal((String) wordTab[i], this);
		}
	}

	public void addText(String text)
	{
		String textToSplit = text.trim();

		if (textToSplit.isEmpty())
		{
			return;
		}

		String[] stringTab = textToSplit.split("\\W+");

		for (int i = 0; i < stringTab.length; i++)
		{
			this.addWord(stringTab[i]);
		}
	}

	public void addText(Text text)
	{
		ITextTraversal addTextTraversal = new ITextTraversal()
		{
			public void traversal(String word, Text text)
			{
				addWord(word);
			}
		};

		text.traversal(addTextTraversal);
	}

	public void addWord(String word)
	{
		this.addWord(word, 1);
	}

	protected void addWord(String word, int occurenceCount)
	{
		String key = word.trim();

		if (key.isEmpty())
		{
			return;
		}
		
		key = key.toLowerCase();

		wordOccurenceSum += occurenceCount;

		Double wordCount = this.occurenceWordMap.get(key);
		if (wordCount == null)
		{
			this.occurenceWordMap.put(key, Double.valueOf(occurenceCount));
			return;
		}

		wordCount += occurenceCount;
		this.occurenceWordMap.put(key, wordCount);
	}

	public boolean removeWord(String word)
	{
		if (!this.occurenceWordMap.containsKey(word))
		{
			return false;
		}

		this.wordOccurenceSum -= this.getOccurenceCount(word);

		for (Map<String, Double> infoMap : this.wordInfoMap.values())
		{
			infoMap.remove(word);
		}

		return true;
	}

	public Text(String name, TextSet textSet)
	{
		this.name = name;
		this.textSet = textSet;

		occurenceWordMap = new HashMap<String, Double>();
		wordInfoMap.put(OCCURENCE_INFO, occurenceWordMap);
	}

	public Text(String name, String text, TextSet textSet)
	{
		this(name, textSet);

		this.addText(text);
	}

	@Override
	public String toString()
	{
		return this.toString(OCCURENCE_INFO);
	}

	public String toString(String info)
	{
		String s = this.name + " : " + System.getProperty("line.separator");

		s += "entries : " + System.getProperty("line.separator");
		Map<String, Double> map = this.wordInfoMap.get(info);
		if (map == null)
		{
			s += "null" + System.getProperty("line.separator");
			return s;
		}
		for (Entry<String, Double> entry : map.entrySet())
		{
			s += entry.getKey() + " : " + entry.getValue()
					+ System.getProperty("line.separator");
		}

		return s;
	}

	public void read(InputStream is) throws IOException
	{
		Reader reader = new InputStreamReader(is);
		BufferedReader bufferedReader = new BufferedReader(reader);

		String line;
		while ((line = bufferedReader.readLine()) != null)
		{
			this.addText(line);
		}
	}
		
	public void read(InputStream is, Map<String, String> translationMap) throws IOException
	{
		if(translationMap == null)
		{
			this.read(is);
			return;
		}
		
		Reader reader = new InputStreamReader(is);
		BufferedReader bufferedReader = new BufferedReader(reader);

		String line;
		while ((line = bufferedReader.readLine()) != null)
		{						
			String textToSplit = line.trim();

			if (textToSplit.isEmpty())
			{
				continue;
			}

			String[] stringTab = textToSplit.split("\\W+");

			for (String word : stringTab)
			{
				String translatedWord = translationMap.get(word);
				if(translatedWord == null)
				{
					continue;
				}
				
				this.addWord(translatedWord);
			}
		}
	}
}